#include "ParseOSM.h"
#include "PlatformConfigGPSTXT.h"
#include "FastXML.h"
#include "StringTable.h"
#include "GpsTypes.h"
#include <vector>
#include <conio.h>
#include <string>

#pragma warning(disable:4996 4100)

#define FLUSH_NODES_COUNT 100000
#define FLUSH_WAYS_COUNT 10000

namespace gpstxt
{

typedef std::vector< const char * > CharPtrVector;	
typedef std::vector< gpstxt::GtU32 > GtU32Vector;

#define ENUM_TAG_LIST_COUNT 96

static const char *enumTagList[ENUM_TAG_LIST_COUNT] =
{
	"wifi",
	"bridge",
	"psv",
	"tunnel",
	"electrified",
	"emergency",
	"boat",
	"foot",
	"cycleway",
	"hgv",
	"fee",
	"motorcar",
	"separated",
	"motor_vehicle",
	"parking",
	"bicycle",
	"oneway",
	"access",
	"historic",
	"man_made",
	"maxspeed",
	"building",
	"artwork",
	"crossing",
	"odbl",
	"product",
	"fishing",
	"information",
	"monitoring_station",
	"sym",
	"denotation",
	"board_type",
	"highway2",
	"timezone",
	"office",
	"sourec",
	"ncn",
	"military",
	"local_ref",
	"site_type",
	"nfd",
	"shelter_type",
	"pcinecta",
	"trail_visibility",
	"sac_scale",
	"tower",
	"industrial",
	"radar",
	"golf_cart",
	"land_use",
	"highwy",
	"name_direction_prefix_3",
	"maxstay",
	"attribution",
	"funcstat",
	"junction",
	"modifier",
	"name2",
	"name_base_4",
	"boundary",
	"destination",
	"pcicbsa",
	"nat_name",
	"species",
	"faa",
	"iata",
	"history",
	"state",
	"business",
	"sidewalk",
	"name_type_3",
	"proposed",
	"religion",
	"generator",
	"golf",
	"restriction",
	"aeroway",
	"border_type",
	"construction",
	"natural",
	"attraction",
	"service",
	"place",
	"railway",
	"power",
	"waterway",
	"tourism",
	"name_type_2",
	"type",
	"surface",
	"barrier",
	"denomination",
	"landuse",
	"name_type_1",
	"highway",
	"amenity"
};


#define MULTI_ENUM_TAG_LIST_COUNT 7
static const char * multiEnumTagList[MULTI_ENUM_TAG_LIST_COUNT] =
{
	"animal",
	"route",
	"leisure",
	"sport",
	"cuisine",
	"shop",
	"name_type"
};

#define IGNORE_TAG_LIST_COUNT 21

static const char *ignoreTagList[IGNORE_TAG_LIST_COUNT] =
{
	"gnis",
	"ref",
	"color",
	"reviewed",
	"mtfcc",
	"mcn_ref",
	"classfp",
	"fixme",
	"tlid",
	"nhd",
	"edited_by",
	"created_by",
	"updated_by",
	"upload_uuid",
	"time",
	"source",
	"cfcc",
	"import_uuid",
	"ios3166-1",
	"history",
	"icao"
};

#define NAME_TYPE_TAG_COUNT 5

static const char *nameTypeTagList[NAME_TYPE_TAG_COUNT] =
{
	"name_type",
	"name_type_1",
	"name_type_2",
	"name_type_3",
	"name_type_4"
};

#define ZIP_TAG_COUNT 8

static const char *zipTagList[ZIP_TAG_COUNT] =
{
	"zip_left",
	"zip_left_1",
	"zip_left_2",
	"zip_left_3",
	"zip_right",
	"zip_right_1",
	"zip_right_2",
	"zip_right_3"
};

#define MULTI_DIRECTION_TAG_COUNT 8

static const char *multiDirectionTagList[MULTI_DIRECTION_TAG_COUNT] =
{
	"name_direction_suffix",
	"name_direction_prefix",
	"name_direction_suffix_1",
	"name_direction_prefix_1",
	"name_direction_suffix_2",
	"name_direction_prefix_2",
	"name_direction_suffix_3",
	"name_direction_prefix_3"
};

static bool isMultiDirectionTag(const char *tag)
{
	bool ret = false;
	for (gpstxt::GtU32 i=0; i<MULTI_DIRECTION_TAG_COUNT; i++)
	{
		if ( strcmp(multiDirectionTagList[i],tag) == 0 )
		{
			ret = true;
			break;
		}
	}
	return ret;
}



static bool isZipTag(const char *tag)
{
	bool ret = false;
	for (gpstxt::GtU32 i=0; i<ZIP_TAG_COUNT; i++)
	{
		if ( strcmp(zipTagList[i],tag) == 0 )
		{
			ret = true;
			break;
		}
	}
	return ret;
}

static bool isNameTypeTag(const char *tag)
{
	bool ret = false;
	for (gpstxt::GtU32 i=0; i<NAME_TYPE_TAG_COUNT; i++)
	{
		if ( strcmp(nameTypeTagList[i],tag) == 0 )
		{
			ret = true;
			break;
		}
	}
	return ret;
}

static bool isIgnoreTag(const char *tag)
{
	bool ret = false;
	for (gpstxt::GtU32 i=0; i<IGNORE_TAG_LIST_COUNT; i++)
	{
		if ( strcmp(ignoreTagList[i],tag) == 0 )
		{
			ret = true;
			break;
		}
	}
	return ret;
}

static bool isEnumTag(const char *tag)
{
	bool ret = false;
	for (gpstxt::GtU32 i=0; i<ENUM_TAG_LIST_COUNT; i++)
	{
		if ( strcmp(enumTagList[i],tag) == 0 )
		{
			ret = true;
			break;
		}
	}
	return ret;
}

static bool isMultiEnumTag(const char *tag)
{
	bool ret = false;
	for (gpstxt::GtU32 i=0; i<MULTI_ENUM_TAG_LIST_COUNT; i++)
	{
		if ( strcmp(multiEnumTagList[i],tag) == 0 )
		{
			ret = true;
			break;
		}
	}
	return ret;
}

static bool hasSubKey(WayTagType type)
{
	bool ret = false;

	switch ( type )
	{
		case WTT_REF:
		case WTT_GNIS:
		case WTT_CENSUS:
		case WTT_WIKIPEDIA:
		case WTT_IS__IN:
		case WTT_NAME:
		case WTT_ADDR:
		case WTT_SOCIAL__NETWORK:
		case WTT_WEBSITE:
		case WTT_FUEL:
		case WTT_OLD__REF:
		case WTT_FIXME:
		case WTT_PHONE:
		case WTT_SOURCE:
		case WTT_EXIT__TO:
		case WTT_ARTWORK:
		case WTT_SPECIES:
		case WTT_RECYCLING:
		case WTT_NFD:
		case WTT_GENERATOR:
		case WTT_TOWER:
		case WTT_HGV:
		case WTT_NOTE:
		case WTT_CAPACITY:
		case WTT_MAXWEIGHT:
		case WTT_NHD:
		case WTT_CONTACT:
		case WTT_ONEWAY:
		case WTT_BUILDING:
		case WTT_CYCLEWAY:
		case WTT_BANE:
		case WTT_NIST:
        	ret = true;
        	break;
	}
	return ret;
}


class _ParseOSM : public ParseOSM, public UANS::UserAllocated, public FAST_XML::FastXml::Callback
{
public:

	typedef stdext::hash_map< size_t, gpstxt::GtU32 > CharCount;

	class TagTracker
	{
	public:
		TagTracker(const char *tag,gpstxt::GtU32 tagValue)
		{
			mTag = tag;
			mTagValue = tagValue;
			mValueLength = 0;
			mSubKeyLength = 0;
		}

		void add(const char *value,const char *subKey)
		{
			addCharCount(value,mValues,mValueLength);
			addCharCount(subKey,mSubKeys,mSubKeyLength);
		}

		void addCharCount(const char *str,CharCount &ccount,size_t &slen)
		{
			if ( str )
			{
				size_t hash = (size_t)str;
				CharCount::iterator found = ccount.find(hash);
				if ( found == ccount.end() )
				{
					ccount[hash] = 1;
					slen+=strlen(str)+1;
				}
				else
				{
					(*found).second++;
				}
			}
		}

		bool isDirection(const char *str) const
		{
			bool ret = false;

			if ( stricmp(str,"n") == 0 ||
				 stricmp(str,"s") == 0 ||
				 stricmp(str,"e") == 0 ||
				 stricmp(str,"w") == 0 ||
				 stricmp(str,"north") == 0 ||
				 stricmp(str,"south") == 0 ||
				 stricmp(str,"east") == 0 ||
				 stricmp(str,"west") == 0 ||
				 stricmp(str,"northeast") == 0 ||
				 stricmp(str,"northwest") == 0 ||
				 stricmp(str,"southeast") == 0 ||
				 stricmp(str,"southwest") == 0 ||
				 stricmp(str,"ne") == 0 ||
				 stricmp(str,"nw") == 0 ||
				 stricmp(str,"se") == 0 ||
				 stricmp(str,"sw") == 0 )
			{
				ret = true;
			}
			return ret;
		}

		bool isBool(const char *str) const
		{
			bool ret = false;
			if ( stricmp(str,"true") == 0 ||
				 stricmp(str,"false") == 0 ||
				 stricmp(str,"yes") == 0 || 
				 stricmp(str,"y") == 0 || 
				 stricmp(str,"n") == 0 || 
				 stricmp(str,"no") == 0 )
			{
				ret = true;
			}
			return ret;
		}

		bool isDigit(char c) const
		{
			return c >= '0' && c <= '9';
		}

		bool isInt(const char *str,bool &isFloat) const
		{
			bool ret = true;
			bool decimal = false;
			if ( *str == '-' ) // if it's leading with a negative sign; that's ok, it's still an integer.
			{
				str++; 
			}
			while ( *str )
			{
				if ( *str == '.' )
				{
					if ( !decimal )
					{
						decimal = true;
						isFloat = true;
					}
					else
					{
						isFloat = false; // can't have two decimal points.
						ret = false;
						break;
					}
				}
				else if ( !isDigit(*str) )
				{
					ret = false;
					break;
				}
				str++;
			}
			return ret;
		}

		bool isIgnoreTag(const char *tag) const
		{
			bool ret = false;
			for (gpstxt::GtU32 i=0; i<IGNORE_TAG_LIST_COUNT; i++)
			{
				if ( strcmp(ignoreTagList[i],tag) == 0 )
				{
					ret = true;
					break;
				}
			}
			return ret;
		}

		bool isEnumTag(const char *tag) const
		{
			bool ret = false;
			for (gpstxt::GtU32 i=0; i<ENUM_TAG_LIST_COUNT; i++)
			{
				if ( strcmp(enumTagList[i],tag) == 0 )
				{
					ret = true;
					break;
				}
			}
			return ret;
		}

		bool isMultiEnumTag(const char *tag) const
		{
			bool ret = false;
			for (gpstxt::GtU32 i=0; i<MULTI_ENUM_TAG_LIST_COUNT; i++)
			{
				if ( strcmp(multiEnumTagList[i],tag) == 0 )
				{
					ret = true;
					break;
				}
			}
			return ret;
		}


		bool isSpecialBool(const char *str) const
		{
			bool ret = false;
			if ( strcmp(str,"separated") == 0 )
			{
				ret = true;
			}
			return ret;
		}

		void report(void)
		{
			gpstxt::GtU32 valueCount = (gpstxt::GtU32)mValues.size();
			gpstxt::GtU32 boolCount=0;
			gpstxt::GtU32 intCount=0;
			gpstxt::GtU32 directionCount=0;
			bool isString = true;
			bool isFloat = false;
			for (CharCount::iterator i=mValues.begin(); i!=mValues.end(); ++i)
			{
				size_t v = (*i).first;
				const char *str = (const char *)v;
				if ( isBool(str) || isSpecialBool(mTag))
				{
					boolCount++;
				}
				if ( isDirection(str) )
				{
					directionCount++;
				}
				if ( isInt(str,isFloat) )
				{
					intCount++;
				}
			}
			mTagType = TT_STRING;
			const char *type = "strings";
			if ( intCount )
			{
				if ( intCount == valueCount )
				{
					if ( isFloat )
					{
						type = "float";
						mTagType = TT_FLOAT;
					}
					else
					{
						type = "integers";
						mTagType = TT_INT;
					}
					isString = false;
				}
				else
				{
					type = "integers+mixed";
				}
			}
			else if ( directionCount && directionCount == valueCount )
			{
				type = "direction";
				mTagType = TT_DIRECTION;
			}
			else if ( boolCount )
			{
				if ( boolCount == valueCount )
				{
					type = "boolean";
					isString = false;
					mTagType =  TT_BOOL;
				}
				else
				{
					type = "boolean+mixed";
				}
			}


			if ( isIgnoreTag(mTag) )
			{
				type = "ignore";
				mTagType = TT_IGNORE;
			}
			else if ( isNameTypeTag(mTag) )
			{
				type = "name";
				mTagType = TT_NAME_TYPE;
			}
			else if ( isZipTag(mTag) )
			{
				type = "zipcode";
				mTagType = TT_ZIP;
			}
			else if ( isMultiDirectionTag(mTag) )
			{
				type = "multi-direction";
				mTagType = TT_MULTI_DIRECTION;
			}
			else if ( isEnumTag(mTag) )
			{
				type = "enum";
				mTagType = TT_ENUM;
			}
			else if ( isMultiEnumTag(mTag) )
			{
				type = "multi-enum";
				mTagType = TT_MULTI_ENUM;
			}
			else if ( strcmp(mTag,"census") == 0 )
			{
				type = "int-pair";
				mTagType = TT_INT_PAIR;
			}

			std::string subKeys = "no_subkeys";
			if ( mSubKeys.size() )
			{
				char scratch[512];
				sprintf_s(scratch,"%d subkeys",mSubKeys.size() );
				subKeys = scratch;
			}


			printf("%-60s : %8d : %-16s : [%-10s] :", mTag, mValues.size(), subKeys.c_str(), type );
			if ( isString )
			{
				gpstxt::GtU32 displayCount = 0;
				for (CharCount::iterator i=mValues.begin(); i!=mValues.end(); ++i)
				{
					size_t v = (*i).first;
					const char *str = (const char *)v;
					printf("%s,", str );
					displayCount++;
					if ( displayCount > 16 )
						break;
				}
			}
			printf("\r\n");
		}

		const char	*mTag;
		size_t		mValueLength;
		CharCount	mValues;
		CharCount	mSubKeys;
		size_t		mSubKeyLength;
		TagType		mTagType;
		gpstxt::GtU32 mTagValue;
	};

	struct TagTrackerSortKey
	{
		inline bool operator() (const TagTracker *struct1, const TagTracker* struct2)
		{
			size_t size1 = struct1->mValues.size();
			size_t size2 = struct2->mValues.size();
			if ( isIgnoreTag(struct1->mTag))
			{
				size1 = 0;
			}
			if ( isIgnoreTag(struct2->mTag))
			{
				size2 = 0;
			}
			if ( size1 == size2 )
			{
				return strcmp(struct1->mTag,struct2->mTag) > 0;
			}
			return (size1 > size2);
		}
	};

	typedef stdext::hash_map< size_t, TagTracker * > TagTrackerMap;
	typedef std::vector< TagTracker *> TagTrackerVector;

	#define MAX_ELEMENT_STACK 32

	#define GPS_SHIFT 23 // number of bits decimal precision to use to convert GPS co-ordinates from floating point degrees into an integer representation
	// We store GPS co-ordinates as fixed point integers so that we can do a fast hash-table lookup
	#define GPS_TO_DEG (1.0f / (gpstxt::GtF32)(1<<GPS_SHIFT))

	class GpsValue
	{
	public:
		GpsValue(void)
		{
			mValue = 0;
		}

		GpsValue(const char *v)
		{
			setValue(v);
		}

		GpsValue(gpstxt::GtF32 v)
		{
			setValue(v);
		}

		void setValue(const char *v)
		{
			gpstxt::GtF32 d = (gpstxt::GtF32)atof(v);
			setValue(d);
		}

		void setValue(gpstxt::GtF32 v)
		{
			GPSTXT_ASSERT( v >= -180 && v <= 180 );
			v+=180;
			v*=(gpstxt::GtF32)(1<<GPS_SHIFT);
			mValue = (gpstxt::GtU32)v;
		}

		gpstxt::GtF32 getDegreeValue(void) const // return the value as floating point degrees.
		{
			return ((gpstxt::GtF32)mValue*GPS_TO_DEG)-180.0f;
		}

		gpstxt::GtU32 getBlockAddress(void) const
		{
			return mValue>>16;
		}

		gpstxt::GtU32 getLocalAddress(void) const
		{
			return mValue &0xFFFF;
		}

		gpstxt::GtU32	mValue;
	};

	class Coordinate
	{
	public:
		gpstxt::GtU32 getBlockAddress(void) const
		{
			return (mLatitude.getBlockAddress() << 16) | mLongitude.getBlockAddress();
		}

		gpstxt::GtU32 getLocalAddress(void) const
		{
			return (mLatitude.getLocalAddress() << 16) | mLongitude.getLocalAddress();
		}

		GpsValue	mLatitude;
		GpsValue	mLongitude;
	};

	class WayTag
	{
	public:
		WayTag(WayTagType type,
			   const char *value)
		{
			mType = type;
			mValue = value;
		}

		WayTagType		mType;
		const char	*mValue;
	};

	typedef std::vector< WayTag > WayTagVector;

	class Node : public UANS::UserAllocated
	{
	public:
		Node(void)
		{
			mId = 0;
		}
		~Node(void)
		{

		}

		void setAttribute(AttributeType type,const char *value)
		{
			switch ( type )
			{
				case AT_ID:
					mId = (gpstxt::GtI32)atoi(value);
					break;
				case AT_LAT:
					mCoordinate.mLatitude.setValue(value);
					break;
				case AT_LON:
					mCoordinate.mLongitude.setValue(value);
					break;
			}
		}

		void addString(WayTagType tt,SubKeyType sk,gpstxt::GtU32 stringIndex)
		{

		}

		void addInt(WayTagType tt,SubKeyType sk,gpstxt::GtI32 v)
		{

		}
		void addFloat(WayTagType tt,SubKeyType sk,gpstxt::GtF32 v)
		{

		}
		void addEnum(WayTagType tt,SubKeyType sk,gpstxt::GtU32 e)
		{

		}
		void addIntPair(WayTagType tt,SubKeyType sk,gpstxt::GtI32 v1,gpstxt::GtI32 v2)
		{

		}
		void addMultiEnum(WayTagType tt,SubKeyType sk,GtU32Vector tagValues)
		{
		}

		gpstxt::GtI32	mId;		// The unique ID of this node.
		Coordinate		mCoordinate;	// it's latitude and longitude location
	};

	typedef std::vector< Node *> NodeVector;

	typedef std::vector< gpstxt::GtI32 > NodeIdVector;

	class Way : public UANS::UserAllocated
	{
	public:
		Way(void)
		{
			mId = 0;
		}
		~Way(void)
		{

		}

		void setAttribute(AttributeType type,const char *value)
		{
			switch ( type )
			{
				case AT_ID:
					mId = (gpstxt::GtI32)atoi(value);
					break;
			}
		}

		void addNode(gpstxt::GtI32 id)
		{
			mNodes.push_back(id);
		}

		void addTag(const WayTag &t)
		{
			mTags.push_back(t);
		}


		gpstxt::GtI32	mId;
		NodeIdVector	mNodes;
		WayTagVector		mTags;
	};

	typedef std::vector< Way * > WayVector;

	class NodeBlock
	{
	public:
		NodeBlock(void)
		{
			mCount = 0;
		}
		gpstxt::GtU32	mCount;
	};

	typedef stdext::hash_map< gpstxt::GtU32, NodeBlock * > NodeBlockMap;

	_ParseOSM(void)
	{
		mStrings.setCaseSensitive(false);
		mEnumStrings.setCaseSensitive(true);
		mNodeStringTable.setCaseSensitive(true);
		initElementType(mElements);
		initAttributeType(mAttributes);
		initWayTagType(mWayTagTypes);
		initSubKeyType(mSubKeys);
		initTagEnums();
		memset(mSubKeyDetect,0,sizeof(mSubKeyDetect));
	}

	virtual ~_ParseOSM(void)
	{
	}

	virtual void processOSM(const char *fname,bool createSourceCode)
	{
		mGenerateSourceCode = createSourceCode;
		mCurrentNode = NULL;
		mCurrentWay = NULL;
		mElementStackIndex = 0;
		mCurrentElementType = ET_NONE;
		mTotalNodeCount = 0;
		mTotalWayCount = 0;
		mTotalRefNodeCount = 0;
		FAST_XML::FastXml *fastXML = createFastXml(this);
		fastXML->processXml(fname);
		flushNodes();
		flushWays();
		printf("Processed %d nodes.\r\n", mTotalNodeCount );
		printf("Processed %d ways.\r\n", mTotalWayCount );
		printf("Processed %d reference nodes.\r\n", mTotalRefNodeCount );

		{
			gpstxt::GtU32 blockCount = mNodeBlocks.size();
			gpstxt::GtU32 minBlock=0xFFFFFFFF;
			gpstxt::GtU32 maxBlock=0;
			gpstxt::GtU32 totalBlockCount=0;
			for (NodeBlockMap::iterator i=mNodeBlocks.begin(); i!=mNodeBlocks.end(); ++i)
			{
				NodeBlock *nb = (*i).second;
				totalBlockCount+=nb->mCount;
				if ( nb->mCount < minBlock )
				{
					minBlock = nb->mCount;
				}
				if ( nb->mCount > maxBlock )
				{
					maxBlock = nb->mCount;
				}
				delete nb;
			}
			printf("Built: %d unique node blocks.\r\n", blockCount);
			printf("Min NodeCount: %d\r\n", minBlock );
			printf("Max NodeCount: %d\r\n", maxBlock );
			printf("Average NodeCount: %d\r\n", totalBlockCount / blockCount );
			mNodeBlocks.clear();
		}


		if ( mGenerateSourceCode )
		{


			{
				StringTableInt::StringUsageVector usage;
				mElements.getStringUsage(usage);
				printf("Encountered %d unique elements.\r\n", usage.size() );
				for (size_t i=0; i<usage.size(); i++)
				{
					StringTableInt::StringUsage &u = usage[i];
					printf("%-60s : %8d\r\n", u.mString, u.mCount );
				}
				printf("\r\n");
			}
			{
				StringTableInt::StringUsageVector usage;
				mAttributes.getStringUsage(usage);
				printf("Encountered %d unique attributes.\r\n", usage.size() );
				for (size_t i=0; i<usage.size(); i++)
				{
					StringTableInt::StringUsage &u = usage[i];
					printf("%-60s : %8d\r\n", u.mString, u.mCount );
				}
				printf("\r\n");
			}
			{
				StringTableInt::StringUsageVector usage;
				mWayTagTypes.getStringUsage(usage);
				for (StringTableInt::StringUsageVector::iterator i=usage.begin(); i!=usage.end(); ++i)
				{
					StringTableInt::StringUsage &usage = (*i);
					if ( isIgnoreTag(usage.mString) )
					{
						usage.mCount = 0;
					}
				}

				std::sort( usage.begin(), usage.end() ); // resort it after removing the ignore tags!

				printf("Encountered %d unique node tagtypes.\r\n", usage.size() );
				for (size_t i=0; i<usage.size(); i++)
				{
					StringTableInt::StringUsage &u = usage[i];
					printf("%-60s : %8d\r\n", u.mString, u.mCount );
				}
				printf("\r\n");

				{
					TagTrackerVector tlist;
					for (TagTrackerMap::iterator i=mWayTagTracker.begin(); i!=mWayTagTracker.end(); ++i)
					{
						TagTracker *tt = (*i).second;
						tlist.push_back(tt);
					}

					printf("Way Detailed Tag Report.\r\n");
					printf("=======================================================================\r\n");

					std::sort(tlist.begin(), tlist.end(), TagTrackerSortKey() );
					for (TagTrackerVector::iterator i=tlist.begin(); i!=tlist.end(); ++i)
					{
						TagTracker *tt = (*i);
						tt->report();
					}
				}

				generateSourceCode();

				for (TagTrackerMap::iterator i=mWayTagTracker.begin(); i!=mWayTagTracker.end(); ++i)
				{
					TagTracker *tt = (*i).second;
					delete tt;
				}
				mWayTagTracker.clear();

			}
		}
	}

	virtual void release(void)
	{
		delete this;
	}

	virtual bool processComment(const char *comment) // encountered a comment in the XML
	{
		GPSTXT_FORCE_PARAMETER_REFERENCE(comment);
		return true;
	}

	// 'element' is the name of the element that is being closed.
	// depth is the recursion depth of this element.
	// Return true to continue processing the XML file.
	// Return false to stop processing the XML file; leaves the read pointer of the stream right after this close tag.
	// The bool 'isError' indicates whether processing was stopped due to an error, or intentionally canceled early.
	virtual bool processClose(const char *element,gpstxt::GtU32 depth,bool &isError)	  // process the 'close' indicator for a previously encountered element
	{
		bool ret = true;

		isError = false;

		GPSTXT_ASSERT( mElementStackIndex > 0 );
		if ( mElementStackIndex > 0 )
		{
			if ( mCurrentElementType == ET_NODE )
			{
				mCurrentNode = NULL;
				if ( mNodes.size() >= FLUSH_NODES_COUNT )
				{
					flushNodes();
				}
			}
			else if ( mCurrentElementType == ET_WAY )
			{
				mCurrentWay = NULL;
				if ( mWays.size() >= FLUSH_WAYS_COUNT )
				{
					flushWays();
				}
			}
			mElementStackIndex--;
			mCurrentElementType = mElementStack[mElementStackIndex];
		}
		if ( kbhit() )
		{
			if ( getch() == 27 )
			{
				printf("User hit escape; exiting early.\r\n");
				ret = false;
			}
		}

		return ret;
	}

	void processAttribute(ElementType etype,AttributeType atype,const char *_value)
	{
#if 0
		if ( etype == ET_TAG && atype == AT_K )
		{
			static gpstxt::GtU32 tcount = 1;
			bool tiger = false;
			//                   012345
			if ( strncmp(_value,"tiger:",6) == 0 )
			{
				_value+=6;
				tiger = true;
			}
			const char *value = _value;
			const char *colon = strchr(_value,':');
			if ( colon )
			{
				static char scratch[512];
				strcpy_s(scratch,512,_value);
				char *scan = strchr(scratch,':');
				if ( scan )
				{
					*scan = 0;
				}
				value = scratch;
			}
			gpstxt::GtU32 v;
			bool found = mWayTagTypes.Get(value,v);
			if ( !found )
			{
				addTag(value);
				printf("Unknown TagType: %s Tiger=%s\r\n", value, tiger ? "true" : "false" );
			}
		}
#endif
	}

	void addAttribute(const char *atr)
	{
		gpstxt::GtU32 v = mAttributes.GetSize()+1;
		mAttributes.Add(atr,v);
	}

	// return true to continue processing the XML document, false to skip.
	virtual bool processElement(
		const char *elementName,   // name of the element
		gpstxt::GtI32 argc,         // number of attributes pairs
		const char **argv,         // list of attributes.
		const char  *elementData,  // element data, null if none
		gpstxt::GtI32 lineno)  // line number in the source XML file
	{
		ElementType etype = ET_NONE;
		gpstxt::GtU32 v;
		bool ok = mElements.Get(elementName,v);
		if ( ok )
		{
			etype = (ElementType)v;
			gpstxt::GtI32 count = argc/2;
			switch ( etype )
			{
				case ET_WAY:
					{
						mCurrentWay = GPSTXT_NEW(Way);
						for (gpstxt::GtI32 i=0; i<count; i++)
						{
							const char *atr = argv[i*2+0];
							const char *value = argv[i*2+1];
							ok = mAttributes.Get(atr,v);
							if ( ok )
							{
								mCurrentWay->setAttribute((AttributeType)v,value);
							}
							else
							{
								printf("Encountered unknown attribute: %s\r\n", atr );
								addAttribute(atr);
							}
						}
						mWays.push_back(mCurrentWay);
					}
					break;
				case ET_NODE:
					{
						mCurrentNode = GPSTXT_NEW(Node);
						for (gpstxt::GtI32 i=0; i<count; i++)
						{
							const char *atr = argv[i*2+0];
							const char *value = argv[i*2+1];
							ok = mAttributes.Get(atr,v);
							if ( ok )
							{
								mCurrentNode->setAttribute((AttributeType)v,value);
							}
							else
							{
								printf("Encountered unknown attribute: %s\r\n", atr );
								addAttribute(atr);
							}
						}
						mNodes.push_back(mCurrentNode);
					}
					break;
				case ET_ND:
					{
						if ( mCurrentWay )
						{
							GPSTXT_ASSERT( count == 1 );
							if ( strcmp(argv[0],"ref") == 0 )
							{
								gpstxt::GtI32 id = (gpstxt::GtI32)atoi(argv[1]);
								mCurrentWay->addNode(id);
							}
							else
							{
								printf("Unexpected attribute for element 'nd'; %s\r\n", argv[0] );
								addAttribute(argv[0]);
							}
						}
					}
					break;
				case ET_TAG:
					{
						GPSTXT_ASSERT( count == 2 );

						if ( count == 2 && strcmp(argv[0],"k") == 0 && strcmp(argv[2],"v") == 0 )
						{
							const char *key = argv[1];
							bool tiger = false;
							//                   012345
							if ( strncmp(key,"tiger:",6) == 0 )
							{
								key+=6;
								tiger = true;
							}
							const char *tag = NULL;
							bool first;
							const char *subKey = NULL;
							static char scratch[512];
							const char *colon = strchr(key,':');
							if ( colon )
							{
								strncpy(scratch,key,512);
								char *c = strchr(scratch,':');
								*c = 0;
								tag = scratch;
								subKey = c+1;
								gpstxt::GtU32 skeyValue;
								if ( !mSubKeys.Get(subKey,skeyValue) )
								{
									v = mSubKeys.GetSize()+1;
									mSubKeys.Add(subKey,v);
								}
							}
							else
							{
								tag = key;
							}
							if ( !mGenerateSourceCode )
							{
								gpstxt::GtU32 v;
								if ( !mWayTagTypes.Get(tag,v) )
								{
									v = mWayTagTypes.GetSize()+1;
									mWayTagTypes.Add(tag,v);
									printf("Unknown WayTagType: %s!\r\n", tag );
								}
								gpstxt::GtU32 sk=0;
								if ( subKey && !mSubKeys.Get(subKey,sk))
								{
									sk = mSubKeys.GetSize()+1;
									mSubKeys.Add(subKey,sk);
									printf("Unknown subkey type '%s'!\r\n", subKey);
								}
								if ( sk >= SKT_LAST )
								{
									sk = 0;
								}

								if ( subKey && v < WTT_LAST )
								{
									if ( mSubKeyDetect[v] == 0 )
									{
										mSubKeyDetect[v] = 1;
										printf("WayTagType: %s uses sub-keys.\r\n", tag );
									}
								}


								SubKeyType skey = (SubKeyType)sk;
								if ( v < WTT_LAST && mCurrentNode ) // if it's a known way tag type..
								{

									const char *value = argv[3];
									TagType t = gWayTagTypes[v];
									WayTagType wtt = (WayTagType)v;

									switch ( t )
									{
										case TT_ENUM:
										case TT_DIRECTION:
										case TT_NAME_TYPE:
											{
												const char *sep = strchr(value,';');
												if ( sep )
												{
													printf("Unexpected separator!  Tag: %s Value: %s\r\n", tag, value );
												}
												gpstxt::GtU32 v = getTagEnum(wtt,value);
												if ( v == 0 )
												{
													printf("Unable to resolve enumeration  Tag: %s Value: %s\r\n", tag, value );
												}
												else
												{
													mCurrentNode->addEnum(wtt,skey,v);
												}
											}
											break;
										case TT_FLOAT:
											{
												gpstxt::GtF32 v = (float)atof(value);
												mCurrentNode->addFloat(wtt,skey,v);
											}
											break;
										case TT_INT:
											{
												gpstxt::GtI32 v = atoi(value);
												mCurrentNode->addInt(wtt,skey,v);
											}
											break;
										case TT_INT_PAIR:
											{
												gpstxt::GtI32 v1 = atoi(value);
												gpstxt::GtI32 v2 = 0;
												const char *scan = strchr(value,';');
												if ( scan )
												{
													scan++;
													v2 = atoi(scan);
												}
												mCurrentNode->addIntPair(wtt,skey,v1,v2);

											}
											break;
										case TT_MULTI_DIRECTION:
										case TT_MULTI_ENUM:
											{
												GtU32Vector tags;

												const char *choice = (const char *)value;
												const char *semi = strchr(choice,';');
												if ( semi )
												{
													char scratch[512];
													char *dest = scratch;
													while ( choice && *choice )
													{
														if ( *choice == ';' )
														{
															*dest = 0;
															char *echoice = stripLeadingTrailingSpaces(scratch);
															gpstxt::GtU32 v = getTagEnum(wtt,echoice);
															if ( v == 0 )
															{
																printf("Unable to resolve enumeration  Tag: %s Value: %s\r\n", tag, echoice );
															}
															else
															{
																tags.push_back(v);
															}
															choice++;
															dest = scratch;
														}
														else
														{
															*dest++ = *choice++;
														}
													}
													*dest = 0;
													char *echoice = stripLeadingTrailingSpaces(scratch);
													gpstxt::GtU32 v = getTagEnum(wtt,echoice);
													if ( v == 0 )
													{
														printf("Unable to resolve enumeration  Tag: %s Value: %s\r\n", tag, echoice );
													}
													else
													{
														tags.push_back(v);
													}
												}
												else
												{
													gpstxt::GtU32 v = getTagEnum(wtt,value);
													if ( v == 0 )
													{
														printf("Unable to resolve enumeration  Tag: %s Value: %s\r\n", tag, value );
													}
													else
													{
														tags.push_back(v);
													}
												}
												if ( !tags.empty() )
												{
													mCurrentNode->addMultiEnum(wtt,skey,tags);
												}
											}
											break;
										case TT_STRING:
											{
												gpstxt::GtU32 v=0;
												if ( !mNodeStringTable.Get(value,v) )
												{
													v = mNodeStringTable.GetSize()+1;
													mNodeStringTable.Add(value,v);
												}
												mCurrentNode->addString(wtt,skey,v);
											}
											break;
									}
								}
							}
							else
							{
								const char *value = mStrings.Get(argv[3],first);

								if ( mCurrentNode )
								{
									gpstxt::GtU32 v;
									if ( !mWayTagTypes.Get(tag,v) )
									{
										v = mWayTagTypes.GetSize()+1;
										mWayTagTypes.Add(tag,v);
									}
									WayTag t((WayTagType)v,value);
									addTagTracker(v,tag,value,subKey,mWayTagTracker);
								}
								else if ( mCurrentWay )
								{
									gpstxt::GtU32 v;
									if ( !mWayTagTypes.Get(tag,v) )
									{
										v = mWayTagTypes.GetSize()+1;
										mWayTagTypes.Add(tag,v);
									}
									WayTag t((WayTagType)v,value);
									addTagTracker(v,tag,value,subKey,mWayTagTracker);
									mCurrentWay->addTag(t);
								}
							}
						}
						else
						{
							printf("Unexpected attribute types for a tag element.\r\n");
						}
					}
					break;
				default:
					if ( mCurrentNode )
					{
					}
					else if ( mCurrentWay )
					{
					}
					else
					{
						for (gpstxt::GtI32 i=0; i<count; i++)
						{
							const char *atr = argv[i*2+0];
							const char *value = argv[i*2+1];
							ok = mAttributes.Get(atr,v);
							if ( ok )
							{
								processAttribute(etype,(AttributeType)v,value);
							}
							else
							{
								printf("Encountered unknown attribute: %s\r\n", atr );
								addAttribute(atr);
							}
						}
					}
					break;
			}
		}
		else
		{
			printf("Encountered unknown Element Type '%s'\r\n", elementName );
			gpstxt::GtU32 v = mElements.GetSize()+1;
			mElements.Add(elementName,v);
		}

		GPSTXT_ASSERT( mElementStackIndex < MAX_ELEMENT_STACK );
		if ( mElementStackIndex < MAX_ELEMENT_STACK )
		{
			mElementStack[mElementStackIndex] = etype;
			mElementStackIndex++;
			mCurrentElementType = etype;
		}

		return true;
	}

	// process the XML declaration header
	virtual bool processXmlDeclaration(
		gpstxt::GtI32 /*argc*/,
		const char ** /*argv*/,
		const char  * /*elementData*/,
		gpstxt::GtI32 /*lineno*/)
	{
		return true;
	}

	virtual bool processDoctype(
		const char * /*rootElement*/, //Root element tag
		const char * /*type*/,        //SYSTEM or PUBLIC
		const char * /*fpi*/,         //Formal Public Identifier
		const char * /*uri*/)         //Path to schema file
	{
		return true;
	}

	virtual void *  fastxml_malloc(size_t size)
	{
		return GPSTXT_ALLOC(size);
	}

	virtual void	fastxml_free(void *mem)
	{
		GPSTXT_FREE(mem);
	}

	void flushNodes(void)
	{
		mTotalNodeCount+=(gpstxt::GtU32)mNodes.size();
		printf("Flushing %d Nodes parsed a total of %d\r\n", mNodes.size(), mTotalNodeCount);
		for (size_t i=0; i<mNodes.size(); i++)
		{
			Node *n = mNodes[i];
			gpstxt::GtU32 blockAddress = n->mCoordinate.getBlockAddress();
			NodeBlock *nb;
			NodeBlockMap::iterator found = mNodeBlocks.find(blockAddress);
			if ( found == mNodeBlocks.end() )
			{
				nb = new NodeBlock;
				mNodeBlocks[blockAddress] = nb;
			}
			else
			{
				nb = (*found).second;
			}
			nb->mCount++;

			delete n;
		}
		mNodes.clear();
		mNodeStringTable.release();
	}

	void flushWays(void)
	{
		mTotalWayCount+=(gpstxt::GtU32)mWays.size();
		printf("Flushing %d Ways parsed a total of %d.\r\n", mWays.size(), mTotalWayCount);
		for (size_t i=0; i<mWays.size(); i++)
		{
			Way *w = mWays[i];
			mTotalRefNodeCount+=(gpstxt::GtU32)w->mNodes.size();
			delete w;
		}
		mWays.clear();
	}

	void addTagTracker(gpstxt::GtU32 tagValue,const char *tag,const char *value,const char *subKey,TagTrackerMap &t)
	{
		bool first;
		tag = mStrings.Get(tag,first);
		size_t hash = (size_t)tag;
		TagTracker *tt = NULL;
		TagTrackerMap::iterator found = t.find(hash);
		if ( found == t.end() )
		{
			tt = new TagTracker(tag,tagValue);
			t[hash] = tt;
		}
		else
		{
			tt = (*found).second;
		}
		tt->add(value,subKey);
	}

	bool isAlphaNumeric(char c) const
	{
		bool ret= false;
		if ( c >= '0' && c <= '9' )
		{
			ret = true;
		}
		else if ( c >= 'A' && c <= 'Z' )
		{
			ret = true;
		}
		else if ( c >= 'a' && c <= 'z' )
		{
			ret = true;
		}
		return ret;
	}

	void generateEnum(FILE *header,FILE *cpp,const char *enumName,const char *enumPrefix,const StringTableInt::StringUsageVector &usage)
	{
		if ( header )
		{
			if ( cpp )
			{
				fprintf(cpp,"\r\n");
				fprintf(cpp,"//*****************************************************************\r\n");
				fprintf(cpp,"void init%s(StringTableInt &types)\r\n", enumName );
				fprintf(cpp,"{\r\n");
			}
			fprintf(header,"//*****************************************************************\r\n");
			fprintf(header,"enum %s {\r\n", enumName );
			fprintf(header,"\t%sNONE,\t\t//[0] NONE\r\n", enumPrefix);

			for (size_t i=0; i<usage.size(); i++)
			{
				const StringTableInt::StringUsage &s = usage[i];
				char source[512];
				char scratch[512];
				strncpy(source,s.mString,512);
				strupr(source);

				const char *scan = source;
				char *dest = scratch;

				while ( *scan )
				{
					if ( !isAlphaNumeric(*scan) )
					{
						if ( *scan == ')' )
						{
							*dest++ = '_';
							*dest++ = 'P';
							*dest++ = '_';
						}
						else if ( *scan == '_')
						{
							*dest++ = '_';
							*dest++ = '_';
						}
						else
						{
							*dest++ = '_';
						}
					}
					else
					{
						*dest++ = *scan;
					}
					scan++;
				}
				*dest = 0;

				fprintf(header,"\t%s%s,\t\t//[%d] %s\r\n", enumPrefix, scratch,i+1,s.mString);

				if ( cpp ) 
				{
					fprintf(cpp,"\t%s.Add(\"%s\",%s%s);\t\t//[%d]\r\n", "types", s.mString, enumPrefix, scratch,i+1 );
				}
			}

			fprintf(header,"\t%sLAST\r\n", enumPrefix );
			fprintf(header,"};\r\n");
			fprintf(header,"\r\n");
			fprintf(header,"void init%s(StringTableInt &types);\r\n",enumName);
			fprintf(header,"//*****************************************************************\r\n");
			fprintf(header,"\r\n");
			if ( cpp )
			{
				fprintf(cpp,"}\r\n");
				fprintf(cpp,"\r\n");
			}

		}
	}

	void generateStructEnum(FILE *header,FILE *cpp,const char *enumName,const StringTableInt::StringUsageVector &usage)
	{
		if ( header )
		{
			if ( cpp )
			{
				fprintf(cpp,"\r\n");
				fprintf(cpp,"//*****************************************************************\r\n");
				fprintf(cpp,"void init%s(StringTableInt &types)\r\n", enumName );
				fprintf(cpp,"{\r\n");
			}
			fprintf(header,"//*****************************************************************\r\n");
			fprintf(header,"// Enumeration Definition for '%s'\r\n", enumName);
			fprintf(header,"//*****************************************************************\r\n");
			fprintf(header,"struct %s {\r\n", enumName );
			fprintf(header,"\tenum {\r\n");
			fprintf(header,"\t\t_NONE,\t\t//[0] NONE\r\n");
			for (size_t i=0; i<usage.size(); i++)
			{
				const StringTableInt::StringUsage &s = usage[i];
				char source[512];
				char scratch[512];
				strncpy(source,s.mString,512);
				strupr(source);

				const char *scan = source;
				char *dest = scratch;
				if ( isdigit(*scan) )
				{
					*dest++ = 'D'; // add an alpha-numeric prefix!
				}
				while ( *scan )
				{
					if ( !isAlphaNumeric(*scan) )
					{
						if ( *scan == ')' )
						{
							*dest++ = '_';
							*dest++ = 'P';
							*dest++ = '_';
						}
						else if ( *scan == '_')
						{
							*dest++ = '_';
							*dest++ = '_';
						}
						else
						{
							*dest++ = '_';
						}
					}
					else
					{
						*dest++ = *scan;
					}
					scan++;
				}
				*dest = 0;

				fprintf(header,"\t\t%s,\t\t//[%d]\r\n", scratch, i, s.mString);
				if ( cpp ) 
				{
					fprintf(cpp,"\t%s.Add(\"%s\",%s::%s);\t\t//[%d]\r\n", "types", s.mString, enumName, scratch, i+1 );
				}
			}
			fprintf(header,"\t\tLAST\r\n");
			fprintf(header,"\t};\r\n");
			fprintf(header,"};\r\n");
			fprintf(header,"\r\n");
			fprintf(header,"void init%s(StringTableInt &types);\r\n",enumName);
			if ( cpp )
			{
				fprintf(cpp,"}\r\n");
				fprintf(cpp,"\r\n");
				fprintf(cpp,"//*****************************************************************\r\n");
				fprintf(cpp,"\r\n");
				fprintf(cpp,"\r\n");
			}
			fprintf(header,"//*****************************************************************\r\n");
			fprintf(header,"\r\n");
			fprintf(header,"\r\n");
		}
	}


	const char *getTagTypeString(TagType tt) const
	{
		const char *ret = "TT_IGNORE";
		switch ( tt )
		{
			case TT_IGNORE: ret = "TT_IGNORE"; break;
			case TT_BOOL: ret = "TT_BOOL"; break;
			case TT_INT: ret = "TT_INT"; break;
			case TT_INT_PAIR: ret = "TT_INT_PAIR"; break;
			case TT_FLOAT: ret = "TT_FLOAT"; break;
			case TT_STRING: ret = "TT_STRING"; break;
			case TT_DIRECTION: ret = "TT_DIRECTION"; break;
			case TT_ENUM: ret = "TT_ENUM"; break;
			case TT_MULTI_ENUM: ret = "TT_MULTI_ENUM"; break;
			case TT_NAME_TYPE: ret = "TT_NAME_TYPE"; break;
			case TT_ZIP: ret = "TT_ZIP"; break;
			case TT_MULTI_DIRECTION: ret = "TT_MULTI_DIRECTION"; break;
		}
		return ret;
	}

	const char *getEnumName(const char *_tag)
	{

		if ( !isAlphaNumeric(*_tag) )
		{
			return "ERROR_ERROR_BAD_TAG_NAME";
		}
		char tagWork[512];
		strncpy(tagWork,_tag,512);
		strlwr(tagWork);
		const char *tag = tagWork;
		char scratch[512];
		char *dest = scratch;

		*dest++ = toupper(*tag++);

		while ( *tag )
		{
			if ( isAlphaNumeric(*tag) )
			{
				*dest++ = *tag++;
			}
			else if ( *tag == ')')
			{
				*dest++ = '_';
				*dest++ = 'P';
				*dest++ = '_';
				tag++;
			}
			else if ( *tag == '_')
			{
				*dest++ = '_';
				*dest++ = '_';
				tag++;
			}
			else
			{
				*dest++ = '_';
				tag++;
			}
		}
		*dest = 0;

		bool first;
		const char *ret = mEnumStrings.Get(scratch,first);

		return ret;
	}

	char * stripLeadingTrailingSpaces(char *scan)
	{
		strlwr(scan); // no case sensitivity!
		while ( *scan && *scan == ' ' ) scan++;
		int len = (int)strlen(scan);
		if ( len == 0 )
		{
			scan = NULL;
		}
		else
		{
			for (int i=len-1; i>=0; i--)
			{
				if ( scan[i] == ' ' )
				{
					scan[i] = 0;
				}
				else
				{
					break;
				}
			}
			len = (int)strlen(scan);
			if ( len == 0 )
			{
				scan = NULL;
			}
		}
		return scan;
	}

	void addItem(StringTableInt &list,const char *e)
	{
		gpstxt::GtU32 v;
		if ( !list.Get(e,v) )
		{
			v = list.GetSize()+1;
			list.Add(e,v);
		}
	}

	void addEnumList(TagType type,const char *choice,StringTableInt &enumList,StringTableInt &directions,StringTableInt &nameTypes)
	{
		char scratch[512];
		strncpy(scratch,choice,512);
		char *e = stripLeadingTrailingSpaces(scratch);
		if ( e )
		{
			switch ( type )
			{
			case TT_MULTI_ENUM:
			case TT_ENUM:
				addItem(enumList,e);
				break;
			case TT_DIRECTION:
			case TT_MULTI_DIRECTION:
				addItem(directions,e);
				break;
			case TT_NAME_TYPE:
				addItem(nameTypes,e);
				break;
			}
		}
	}

	void generateSourceCode(void)
	{
		FILE *fph = fopen("GpsTypes.h","wb");
		FILE *cpp = fopen("GpsTypes.cpp", "wb");

		if ( fph )
		{
			if ( cpp )
			{
				fprintf(cpp,"#include \"GpsTypes.h\"\r\n");
				fprintf(cpp,"namespace gpstxt {\r\n");
			}

			fprintf(fph,"#ifndef GPS_TYPES_H\r\n");
			fprintf(fph,"#define GPS_TYPES_H\r\n");
			fprintf(fph,"\r\n");

			fprintf(fph,"#include \"StringTable.h\"\r\n");

			fprintf(fph,"namespace gpstxt\r\n");
			fprintf(fph,"{\r\n");
			fprintf(fph,"\r\n");

			{
			fprintf(fph,"\r\n");
			fprintf(fph,"	enum TagType\r\n");
			fprintf(fph,"	{\r\n");
			fprintf(fph,"		TT_IGNORE ,         // this tag should be completely ignored.\r\n");
			fprintf(fph,"		TT_BOOL ,           // this tag is a boolean\r\n");
			fprintf(fph,"		TT_INT ,             // this tag is an integer.\r\n");
			fprintf(fph,"		TT_INT_PAIR ,       // used to the census\r\n");
			fprintf(fph,"		TT_FLOAT ,          // This tag is a floating point number.\r\n");
			fprintf(fph,"		TT_STRING ,         // This tag is a retained string.\r\n");
			fprintf(fph,"		TT_DIRECTION,       //Contains directions like N/S/E/W\r\n");
			fprintf(fph,"		TT_ENUM,            //Enumerated types\r\n");
			fprintf(fph,"		TT_MULTI_ENUM,      //Multiple Enumerated types\r\n");
			fprintf(fph,"		TT_NAME_TYPE,       //Is a name type field\r\n");
			fprintf(fph,"		TT_ZIP,             //Series of zip code\r\n");
			fprintf(fph,"		TT_MULTI_DIRECTION  //multiple direction prefix/suffix tags\r\n");
			fprintf(fph,"	};\r\n");
			fprintf(fph,"\r\n");
			}

			{
				StringTableInt::StringUsageVector usage;
				mElements.getStringUsage(usage);
				generateEnum(fph,cpp,"ElementType","ET_",usage);
			}
			{
				StringTableInt::StringUsageVector usage;
				mAttributes.getStringUsage(usage);
				generateEnum(fph,cpp,"AttributeType","AT_",usage);
			}

			{
				StringTableInt::StringUsageVector usage;
				mWayTagTypes.getStringUsage(usage);
				for (StringTableInt::StringUsageVector::iterator i=usage.begin(); i!=usage.end(); ++i)
				{
					StringTableInt::StringUsage &usage = (*i);
					if ( isIgnoreTag(usage.mString) )
					{
						usage.mCount = 0;
					}
				}
				std::sort( usage.begin(), usage.end() ); // resort it after removing the ignore tags!
				generateEnum(fph,cpp,"WayTagType","WTT_",usage);
			}

			{
				StringTableInt::StringUsageVector usage;
				mSubKeys.getStringUsage(usage);
				generateEnum(fph,cpp,"SubKeyType","SKT_",usage);
			}

			// ok, now time to generate the enums for enumerated types 
			std::vector< const char * > enumNameList;
			
			{
				StringTableInt directions;
				StringTableInt nameTypes;

				for (TagTrackerMap::iterator i=mWayTagTracker.begin(); i!=mWayTagTracker.end(); ++i)
				{
					TagTracker *tt = (*i).second;
					TagType type = tt->mTagType;

					if ( type == TT_ENUM || type == TT_MULTI_ENUM || type == TT_DIRECTION || type == TT_NAME_TYPE || type == TT_MULTI_DIRECTION )
					{

						const char *enumName = getEnumName(tt->mTag);

						StringTableInt enumList;
						for (CharCount::iterator i=tt->mValues.begin(); i!=tt->mValues.end(); ++i)
						{
							size_t c = (*i).first;
							const char *choice = (const char *)c;
							const char *semi = strchr(choice,';');
							if ( semi )
							{
								char scratch[512];
								char *dest = scratch;
								while ( choice && *choice )
								{
									if ( *choice == ';' )
									{
										*dest = 0;
										addEnumList(type,scratch,enumList,directions,nameTypes);
										choice++;
										dest = scratch;
									}
									else
									{
										*dest++ = *choice++;
									}
								}
								*dest = 0;
								addEnumList(type,scratch,enumList,directions,nameTypes);
							}
							else
							{
								addEnumList(type,choice,enumList,directions,nameTypes);
							}
						}

						if ( enumList.GetSize() > 0 )
						{
							StringTableInt::StringUsageVector usage;
							enumList.getStringUsage(usage);
							enumNameList.push_back(enumName);
							generateStructEnum(fph,cpp,enumName,usage);
						}
					}
				}

				if ( directions.GetSize() > 0 )
				{
					StringTableInt::StringUsageVector usage;
					directions.getStringUsage(usage);
					generateStructEnum(fph,cpp,"DirectionType",usage);
				}

				if ( nameTypes.GetSize() > 0 )
				{
					StringTableInt::StringUsageVector usage;
					nameTypes.getStringUsage(usage);
					generateStructEnum(fph,cpp,"NameType",usage);
				}

			}


			
			fprintf(fph,"\r\n");
			fprintf(fph,"extern TagType gWayTagTypes[WTT_LAST];\r\n");
			fprintf(fph,"\r\n");
			fprintf(fph,"void initTagEnums(void);\r\n");
			fprintf(fph,"\r\n");
			fprintf(fph,"gpstxt::GtU32 getTagEnum(WayTagType type,const char *str);\r\n");
			fprintf(fph,"\r\n");

			if ( cpp )
			{
				{
					fprintf(cpp,"TagType gWayTagTypes[WTT_LAST] = {\r\n");
					for (gpstxt::GtU32 i=WTT_NONE; i<WTT_LAST; i++)
					{
						TagType type = TT_IGNORE;
						const char *tagName = "NONE";
						const char *tagNameShouldBe = mWayTagTypes.getString(i);

						for (TagTrackerMap::iterator j=mWayTagTracker.begin(); j!=mWayTagTracker.end(); j++)
						{
							TagTracker *tt = (*j).second;
							if ( tt->mTagValue == i )
							{
								type = tt->mTagType;
								tagName = tt->mTag;
								break;
							}
						}

						char scratch[512];
						strncpy(scratch,getEnumName(tagName),512);
						strupr(scratch);

						if ( (i+1) < WTT_LAST )
						{
							fprintf(cpp,"\t%s,\t\t// [%d] WTT_%s\r\n", getTagTypeString(type), i, scratch );
						}
						else
						{
							fprintf(cpp,"\t%s\t\t// [%d] WTT_%s\r\n", getTagTypeString(type), i, scratch );
						}

					}
					fprintf(cpp,"};\r\n");



					fprintf(cpp,"static StringTableInt gWayTagEnums[WTT_LAST];\r\n");
					fprintf(cpp,"static StringTableInt gNameTypeEnums;\r\n");
					fprintf(cpp,"static StringTableInt gDirectionTypeEnums;\r\n");
					fprintf(cpp,"\r\n");
					fprintf(cpp,"void initTagEnums(void)\r\n");
					fprintf(cpp,"{\r\n");

					for (size_t i=0; i<enumNameList.size(); i++)
					{
						const char *enumName = enumNameList[i];
						char scratch[512];
						strncpy(scratch,enumName,512);
						strupr(scratch);
						fprintf(cpp,"\tinit%s(gWayTagEnums[WTT_%s]);\r\n", enumName, scratch);
					}
					fprintf(cpp,"	initDirectionType(gDirectionTypeEnums);\r\n");
					fprintf(cpp,"	initNameType(gNameTypeEnums);\r\n");
					fprintf(cpp,"}\r\n");
					fprintf(cpp,"\r\n");
					fprintf(cpp,"gpstxt::GtU32 getTagEnum(WayTagType type,const char *str)\r\n");
					fprintf(cpp,"{\r\n");
					fprintf(cpp,"	gpstxt::GtU32 ret = 0;\r\n");
					fprintf(cpp,"\r\n");
					fprintf(cpp,"	switch ( gWayTagTypes[type] )\r\n");
					fprintf(cpp,"	{\r\n");
					fprintf(cpp,"	case TT_ENUM:\r\n");
					fprintf(cpp,"	case TT_MULTI_ENUM:\r\n");
					fprintf(cpp,"		gWayTagEnums[type].Get(str,ret);\r\n");
					fprintf(cpp,"		break;\r\n");
					fprintf(cpp,"	case TT_DIRECTION:\r\n");
					fprintf(cpp,"	case TT_MULTI_DIRECTION:\r\n");
					fprintf(cpp,"		gDirectionTypeEnums.Get(str,ret);\r\n");
					fprintf(cpp,"		break;\r\n");
					fprintf(cpp,"	case TT_NAME_TYPE:\r\n");
					fprintf(cpp,"		gNameTypeEnums.Get(str,ret);\r\n");
					fprintf(cpp,"		break;\r\n");
					fprintf(cpp,"\r\n");
					fprintf(cpp,"	}\r\n");
					fprintf(cpp,"\r\n");
					fprintf(cpp,"	return ret;\r\n");
					fprintf(cpp,"}\r\n");

				}
			}

			fprintf(fph,"\r\n");
			fprintf(fph,"\r\n");


			fprintf(fph,"\r\n");
			fprintf(fph,"}; // End of gpstxt namespace.\r\n");
			fprintf(fph,"#endif\r\n");
			fclose(fph);

			if ( cpp )
			{
				fprintf(cpp,"}; // End of gpstxt namespace.\r\n");
				fclose(cpp);
			}
		}
	}

public:
	StringTable			mStrings;
	StringTable			mEnumStrings;

	StringTableInt		mWayTagTypes;
	StringTableInt		mElements;
	StringTableInt		mAttributes;
	StringTableInt		mSubKeys;
	bool				mGenerateSourceCode;
	Way					*mCurrentWay;
	Node				*mCurrentNode;
	NodeVector			mNodes;
	WayVector			mWays;
	gpstxt::GtU32		mElementStackIndex;
	ElementType			mCurrentElementType;
	ElementType			mElementStack[MAX_ELEMENT_STACK];
	gpstxt::GtU32		mTotalNodeCount;
	gpstxt::GtU32		mTotalWayCount;
	gpstxt::GtU32		mTotalRefNodeCount;
	GtU32Vector			mTagCount;
	TagTrackerMap		mWayTagTracker;
	NodeBlockMap		mNodeBlocks;
	StringTableInt		mNodeStringTable;
	char				mSubKeyDetect[WTT_LAST];
};

ParseOSM * createParseOSM(void)
{
	_ParseOSM *p = GPSTXT_NEW(_ParseOSM);
	return static_cast< ParseOSM *>(p);
}

}; // end of gpstxt namespace
